use std::str::FromStr;

use actix_web::{web, HttpResponse};
use wither::bson::oid::ObjectId;

use crate::{
    errors::cons_error,
    models::{
        article::{ArticleModelDTO, ArticleSectionModelDTO},
        blacklist::BlacklistContent,
        user::{NormalUserModel, UserRole},
    },
    services::{
        article_service::{
            ArticleService, CreateSectionSvrReq, UpdateArticleSvrReq, UpdateSectionSvrReq,
        },
        black_list_service::{BlacklistService, CheckInBlackListSvrReq},
    },
    utils::authorize::{
        check_user_has_permission_with_object_id, check_user_has_section_permission,
    },
    AppError, AppResult, PaginationQuery,
};

mod ctrl_struct {
    use std::str::FromStr;

    use serde_json::Value;
    use wither::bson::oid::ObjectId;

    use crate::{
        models::{article::ArticleAuthor, Assets},
        services::article_service::{
            CreateArticleSvrReq, CreateSectionSvrReq, UpdateArticleSvrReq, UpdateSectionSvrReq,
        },
        utils::db_utils::json_to_document,
        AppError, AppResult,
    };

    define_request_struct!(SectionDetailReq { section_id: String });
    define_filter_request_struct!(SearchAllSectionReq {});
    define_filter_request_struct!(SearchAllArticleReq {
        section_id:Option<ObjectId>
    });
    define_request_struct!(CreateSectionReq {
        name:String,
        description:Option<String>,
        color:Option<String>
    });
    impl From<CreateSectionReq> for CreateSectionSvrReq {
        fn from(r: CreateSectionReq) -> Self {
            Self {
                name: r.name,
                description: r.description,
                color: r.color.unwrap_or_else(|| Self::default().color),
            }
        }
    }
    define_request_struct!(UpdateSectionReq {
        section_id:String,
        name:Option<String>,
        description:Option<String>,
        color:Option<String>
    });

    impl TryFrom<UpdateSectionReq> for UpdateSectionSvrReq {
        type Error = AppError;
        fn try_from(value: UpdateSectionReq) -> Result<Self, Self::Error> {
            let object_id = ObjectId::from_str(&value.section_id)?;
            Ok(Self {
                id: object_id,
                name: value.name,
                description: value.description,
                color: value.color,
            })
        }
    }

    define_request_struct!(DeleteSectionReq { section_id: String });

    define_request_struct!(ArticleDetailReq { article_id: String });
    define_request_struct!(CreateArticleReq {
        section_id: Option<String>,
        title: String,
        cover_image: Option<Assets>,
        description: Option<String>,
        content: Option<Value>
    });

    impl CreateArticleReq {
        pub fn try_to_svr_req<T: Into<ArticleAuthor>>(
            self,
            author: T,
        ) -> AppResult<CreateArticleSvrReq> {
            let section_id = match self.section_id.as_deref().map(ObjectId::from_str) {
                Some(Ok(id)) => Some(id),
                Some(Err(e)) => return Err(e.into()),
                None => None,
            };
            Ok(CreateArticleSvrReq {
                section_id,
                title: self.title,
                cover_image: self.cover_image,
                description: self.description,
                content: self.content.as_ref().and_then(json_to_document),
                author: author.into(),
            })
        }
    }

    define_request_struct!(DeleteArticleReq { article_id: String });
    define_request_struct!(RecoverArticleReq { article_id: String });

    define_request_struct!(UpdateArticleReq {
        article_id:String,
        section_id: Option<String>,
        title: Option<String>,
        cover_image: Option<Assets>,
        description: Option<String>,
        content: Option<Value>
    });

    impl TryFrom<UpdateArticleReq> for UpdateArticleSvrReq {
        type Error = AppError;
        fn try_from(value: UpdateArticleReq) -> Result<Self, Self::Error> {
            let article_id = ObjectId::from_str(&value.article_id)?;
            let section_id = match value.section_id.as_deref().map(ObjectId::from_str) {
                Some(res) => Some(res?),
                None => None,
            };
            Ok(Self {
                id: article_id,
                section_id,
                title: value.title,
                cover_image: value.cover_image,
                description: value.description,
                content: value.content.as_ref().and_then(json_to_document),
            })
        }
    }

    define_request_struct!(WatchArticleReq { article_id: String });
    define_request_struct!(WatchSectionReq { section_id: String });
}
async fn section_detail(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::SectionDetailReq>,
) -> AppResult<HttpResponse> {
    let section_id = ObjectId::from_str(&req.section_id)?;
    let res = svr.section_detail(section_id).await?;
    Ok(HttpResponse::Ok().json(ArticleSectionModelDTO::from(res)))
}
/// ## 搜索所有文章板块
async fn search_all_section(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::SearchAllSectionReq>,
    query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let res = svr
        .search_all_section(
            req.get_filter_as_document_use_default(),
            &query.into_inner(),
        )
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<ArticleSectionModelDTO>()))
}

/// ## 创建文章板块
async fn create_section(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::CreateSectionReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    if !check_user_has_section_permission(&user, None, UserRole::Admin) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr
        .create_section(CreateSectionSvrReq::from(req.into_inner()))
        .await?;

    Ok(HttpResponse::Ok().json(ArticleSectionModelDTO::from(res)))
}

/// ## 更新板块信息
async fn update_section(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::UpdateSectionReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let req_svr = UpdateSectionSvrReq::try_from(req.into_inner())?;
    if !check_user_has_section_permission(user, req_svr.id, UserRole::LeaderOrAdmin) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.update_section(req_svr).await?;
    Ok(HttpResponse::Ok().json(ArticleSectionModelDTO::from(res)))
}

/// ## 删除板块
async fn delete_section(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::DeleteSectionReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let section_id = ObjectId::from_str(&req.section_id)?;
    if !check_user_has_section_permission(user, section_id, UserRole::Admin) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.delete_section(section_id).await?;
    Ok(HttpResponse::Ok().json(ArticleSectionModelDTO::from(res)))
}

async fn watch_section_record(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::WatchSectionReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let section_id = ObjectId::from_str(&req.section_id)?;
    let res = svr.watch_section_record(section_id, user).await?;
    Ok(HttpResponse::Ok().json(ArticleSectionModelDTO::from(res)))
}
async fn article_detail(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::ArticleDetailReq>,
) -> AppResult<HttpResponse> {
    let article_id = ObjectId::from_str(&req.article_id)?;
    let res = svr.article_detail(article_id).await?;
    Ok(HttpResponse::Ok().json(ArticleModelDTO::from(res)))
}
/// ## 搜索所有的文章
async fn search_all_article(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::SearchAllArticleReq>,
    query: web::Query<PaginationQuery>,
) -> AppResult<HttpResponse> {
    let res = svr
        .search_all_article(
            req.section_id,
            req.get_filter_as_document_use_default(),
            &query.into_inner(),
        )
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<ArticleModelDTO>()))
}

/// ## 创建文章接口
async fn create_article(
    svr: web::Data<ArticleService>,
    b_svr: web::Data<BlacklistService>,
    req: web::Json<ctrl_struct::CreateArticleReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let svr_req = req.into_inner().try_to_svr_req(user.clone())?;
    if let Some(section_id) = svr_req.section_id {
        let in_blacklist = b_svr
            .check_in_blacklist(CheckInBlackListSvrReq {
                target_id: user.id,
                content: BlacklistContent::Article { section_id },
            })
            .await?;
        if in_blacklist {
            return Err(AppError::no_permission(cons_error::MSG_IN_BLACK_LIST));
        }
    } else if !check_user_has_permission_with_object_id(user, None, UserRole::Admin) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }

    let res = svr.create_article(svr_req).await?;
    Ok(HttpResponse::Ok().json(ArticleModelDTO::from(res)))
}
/// ## 更新文章接口
async fn update_article(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::UpdateArticleReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let svr_req: UpdateArticleSvrReq = req.into_inner().try_into()?;
    let article = svr
        .find_one_article(svr_req.id, None)
        .await?
        .ok_or(AppError::ConsError(cons_error::MSG_ARTICLE_IS_NOT_FOUND))?;
    if !check_user_has_section_permission(&user, article.section_id, UserRole::LeaderOrAdmin)
        && article.author.id != svr_req.id
    {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.update_article(svr_req).await?;
    Ok(HttpResponse::Ok().json(ArticleModelDTO::from(res)))
}

/// ## 删除文章接口
async fn delete_article(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::DeleteArticleReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let article_id = ObjectId::from_str(&req.article_id)?;
    let article = svr
        .find_one_article(article_id, None)
        .await?
        .ok_or(AppError::ConsError(cons_error::MSG_ARTICLE_IS_NOT_FOUND))?;
    if !check_user_has_section_permission(&user, article.section_id, UserRole::LeaderOrAdmin)
        && article.author.id != article_id
    {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.delete_article(article_id).await?;
    Ok(HttpResponse::Ok().json(ArticleModelDTO::from(res)))
}

/// ## 恢复被删除的文章
async fn recover_article(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::RecoverArticleReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let article_id = ObjectId::from_str(&req.article_id)?;
    let model = svr
        .find_one_article(article_id, None)
        .await?
        .ok_or_else(|| AppError::not_found(cons_error::MSG_ARTICLE_IS_NOT_FOUND))?;
    if !check_user_has_section_permission(user, model.section_id, UserRole::LeaderOrAdmin) {
        return Err(AppError::no_permission(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let res = svr.recover_article(article_id).await?;
    Ok(HttpResponse::Ok().json(ArticleModelDTO::from(res)))
}

async fn watch_article_record(
    svr: web::Data<ArticleService>,
    req: web::Json<ctrl_struct::WatchArticleReq>,
    user: NormalUserModel,
) -> AppResult<HttpResponse> {
    let article_id = ObjectId::from_str(&req.article_id)?;
    let res = svr.watch_article_record(article_id, user).await?;
    Ok(HttpResponse::Ok().json(ArticleModelDTO::from(res)))
}

pub fn configure_article_controller(config: &mut web::ServiceConfig) {
    config
        .service(
            web::scope("/section")
                .route("/search", web::post().to(search_all_section))
                .route("/detail", web::post().to(section_detail))
                .route("/create", web::post().to(create_section))
                .route("/update", web::post().to(update_section))
                .route("/delete", web::post().to(delete_section))
                .route("/watch", web::post().to(watch_section_record)),
        )
        .service(
            web::scope("/article")
                .route("/search", web::post().to(search_all_article))
                .route("/detail", web::post().to(article_detail))
                .route("/create", web::post().to(create_article))
                .route("/update", web::post().to(update_article))
                .route("/delete", web::post().to(delete_article))
                .route("/recover", web::post().to(recover_article))
                .route("/watch", web::post().to(watch_article_record)),
        );
}
